﻿using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
using Microsoft.Practices.Prism.Commands;
using Microsoft.Practices.Prism.Events;
using TaskManager.Event;
using TaskManager.UI;

namespace TaskManager.Control
{
    public class TaskViewModel : ViewModelBase
    {
        #region Properties

        #region Id

        private string _id;

        /// <summary>
        /// Unique id of the task
        /// </summary>
        public string Id
        {
            get { return _id; }
            set
            {
                if (_id == value)
                    return;

                _id = value;
                OnPropertyChanged("Id");
            }
        }

        #endregion

        #region Name

        private string _name;

        /// <summary>
        /// Name of the task
        /// </summary>
        public string Name
        {
            get { return _name; }
            set
            {
                if (_name == value)
                    return;

                _name = value;
                OnPropertyChanged("Name");
            }
        }

        #endregion

        #region Error

        private string _error;

        /// <summary>
        /// Error messages when task failed
        /// </summary>
        public string Error
        {
            get { return _error; }
            set
            {
                if (_error == value)
                    return;

                _error = value;
                OnPropertyChanged("Error");
            }
        }

        #endregion

        #region IsIndeterminate

        private bool _isInderterminate;

        /// <summary>
        /// Gets or sets whether the ProgressBar shows actual values or generic, continuous progress feedback.
        /// </summary>
        public bool IsIndeterminate
        {
            get { return _isInderterminate; }
            set
            {
                if (_isInderterminate == value)
                    return;

                _isInderterminate = value;
                OnPropertyChanged("IsIndeterminate");
            }
        }

        #endregion

        #region IsCancelPossible

        private bool _isCancelPossible;

        /// <summary>
        /// Gets or sets whether the task can be cancel or not
        /// </summary>
        public bool IsCancelPossible
        {
            get { return _isCancelPossible; }
            set
            {
                if (_isCancelPossible == value)
                    return;

                _isCancelPossible = value;
                OnPropertyChanged("IsCancelPossible");
            }
        }

        #endregion

        #region IsClearPossible

        private bool _isClearPossible;

        /// <summary>
        /// Gets or sets whether the task can be clear or not in the logger
        /// </summary>
        public bool IsClearPossible
        {
            get { return _isClearPossible; }
            set
            {
                if (_isClearPossible == value)
                    return;

                _isClearPossible = value;
                OnPropertyChanged("IsClearPossible");
            }
        }

        #endregion

        #region Percent

        private int _percent;

        /// <summary>
        /// Gets or sets the actual progress of the ProgressBar
        /// </summary>
        public int Percent
        {
            get { return _percent; }
            set
            {
                if (_percent == value)
                    return;

                _percent = value;
                OnPropertyChanged("Percent");
            }
        }

        #endregion

        #region Status

        private TaskEventArgsType _status;

        /// <summary>
        /// Status when the tasks finished
        /// </summary>
        public TaskEventArgsType Status
        {
            get { return _status; }
            set
            {
                _status = value;
                OnPropertyChanged("Status");
            }
        }

        #endregion

        public List<int> Ticks;

        /// <summary>
        /// Current asynchronous operation
        /// </summary>
        public Task<object> CurrentTask;

        /// <summary>
        /// Action launch on worker thread
        /// </summary>
        public Func<object> BeginAction;

        /// <summary>
        /// Action lauch on thread UI
        /// </summary>
        public Action<Task<object>> EndAction;

        /// <summary>
        /// Action lauch when user request cancel
        /// </summary>
        public Action CancelAction;

        /// <summary>
        /// Cancel current task
        /// </summary>
        private DelegateCommand _cancelCommand;

        /// <summary>
        /// Clear current task from the logger
        /// </summary>
        private DelegateCommand _clearCommand;

        public CancellationTokenSource TokenSource;

        public IEventAggregator EventAggregator;

        #endregion

        #region Constructor
        
        /// <summary>
        /// Create a new task
        /// </summary>
        /// <param name="name">Task name</param>
        /// <param name="beginAction">Action when task began (in background thread)</param>
        /// <param name="endAction">Action when task ended (in UI thread)</param>
        /// <param name="cancelAction">Action when task cancelled (in background thread)</param>
        /// <param name="isIndeterminate"></param>
        /// <param name="isCancelPossible"></param>
        public TaskViewModel(string name, Func<object> beginAction, Action<Task<object>> endAction, Action cancelAction = null, bool isIndeterminate = true, bool isCancelPossible = true)
        {
            Id = GenerateId();
            Name = name;
            BeginAction = beginAction;
            EndAction = endAction;
            CancelAction = cancelAction;
            Ticks = new List<int>();
            IsIndeterminate = isIndeterminate;
            IsCancelPossible = isCancelPossible;
        }

        /// <summary>
        /// Generate random identifier
        /// </summary>
        /// <returns>random identifier</returns>
        private string GenerateId()
        {
            long i = 1;
            foreach (byte b in Guid.NewGuid().ToByteArray())
            {
                i *= (b + 1);
            }
            return string.Format("{0:x}", i - DateTime.Now.Ticks);
        }

        #endregion

        #region Commands

        public ICommand CancelCommand
        {
            get
            {
                if (_cancelCommand == null)
                {
                    _cancelCommand = new DelegateCommand(Cancel, CanCancel);
                }
                return _cancelCommand;
            }
        }

        public ICommand ClearCommand
        {
            get
            {
                if (_clearCommand == null)
                {
                    _clearCommand = new DelegateCommand(Clear, CanClear);
                }
                return _clearCommand;
            }
        }

        #endregion

        #region Actions

        public bool CanCancel()
        {
            return !CurrentTask.IsCompleted && IsCancelPossible;
        }

        /// <summary>
        /// Cancel task
        /// </summary>
        public void Cancel()
        {
            TokenSource.Cancel();
            if (EventAggregator != null)
                EventAggregator.GetEvent<TaskEvent>().Publish(new TaskEventArgs(TaskEventArgsType.Canceled, Id));
        }

        public bool CanClear()
        {
            return IsClearPossible;
        }

        /// <summary>
        /// Clear task
        /// </summary>
        public void Clear()
        {
            if (EventAggregator != null)
                EventAggregator.GetEvent<TaskEvent>().Publish(new TaskEventArgs(TaskEventArgsType.Cleared, Id));
        }

        #endregion

    }
}
